package com.ceair.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.security.authentication.AbstractAuthenticationToken;
import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.method.configuration.EnableReactiveMethodSecurity;
import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;
import org.springframework.security.config.web.server.ServerHttpSecurity;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter;
import org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter;
import org.springframework.security.oauth2.server.resource.authentication.ReactiveJwtAuthenticationConverterAdapter;
import org.springframework.security.web.server.SecurityWebFilterChain;
import reactor.core.publisher.Mono;

/**
 * @author wangbaohai
 * @ClassName ResourceServerConfig
 * @description: 资源服务器配置, 主要作用是网关的接口鉴权
 * @date 2024年11月28日
 * @version: 1.0.0
 */
@Configuration
@EnableWebFluxSecurity
@EnableReactiveMethodSecurity
public class ResourceServerConfig {

    /**
     * 配置Spring Security过滤链
     *
     * @param http 用于配置服务器HTTP安全的实例
     * @return 构建的SecurityWebFilterChain实例
     */
    @Bean
    public SecurityWebFilterChain defaultSecurityFilterChain(ServerHttpSecurity http) {
        // 禁用csrf与cors
        http.csrf(ServerHttpSecurity.CsrfSpec::disable);
        http.cors(ServerHttpSecurity.CorsSpec::disable);

        // 配置中禁用 X-Frame-Options,允许 http://127.0.0.1:5173 来源的内嵌请求
        http.headers(headers -> headers
                .contentSecurityPolicy(csp -> csp
                        .policyDirectives("frame-ancestors 'self' http://127.0.0.1:5173 http://192.168.30.129:5173")
                ));

        // 开启全局验证
        http.authorizeExchange((authorize) -> authorize
                //全部需要认证
                .pathMatchers(
                        "/webjars/**",
                        "/v3/api-docs/**",
                        "/swagger-ui/**",
                        "/swagger-ui.html",
                        "/*/v3/api-docs/**").permitAll()
                .anyExchange().authenticated());

        // 开启OAuth2登录
        http.oauth2Login(Customizer.withDefaults());

        // 配置OAuth2资源服务器支持JWT，并自定义JWT认证转换器
        http.oauth2ResourceServer((resourceServer) -> resourceServer.jwt(jwt ->
                jwt.jwtAuthenticationConverter(grantedAuthoritiesExtractor())));

        // 构建并返回配置好的SecurityWebFilterChain
        return http.build();
    }

    /**
     * 配置JWT权限信息提取器
     * <p>
     * 本方法旨在创建并配置一个转换器，用于从JWT中提取权限信息，并将其转换为Spring Security所需的认证令牌格式
     * 通过这个转换器，我们可以自定义如何从JWT的claims中提取权限信息，以及为权限信息设置前缀
     *
     * @return Converter<Jwt, Mono < AbstractAuthenticationToken>> 返回一个JWT权限信息提取器，它将JWT转换为响应式的认证令牌
     */
    public Converter<Jwt, Mono<AbstractAuthenticationToken>> grantedAuthoritiesExtractor() {
        // 创建JWT权限转换器实例
        JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
        // 设置解析权限信息的前缀，设置为空是去掉前缀
        grantedAuthoritiesConverter.setAuthorityPrefix("");
        // 设置权限信息在jwt claims中的key
        grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

        // 创建JWT认证转换器实例
        JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
        // 设置JWT权限转换器，以便JWT认证转换器知道如何提取和转换权限信息
        jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);

        // 返回适应于响应式编程的JWT认证转换器适配器
        return new ReactiveJwtAuthenticationConverterAdapter(jwtAuthenticationConverter);
    }

}
